/*
 * Copyright (c) 2022. China Mobile (SuZhou) Software Technology Co.,Ltd. All rights reserved.
 * Lakehouse is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

package com.chinamobile.cmss.lakehouse.core.pipeline;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class PipelineConfig implements ApplicationContextAware {

    /**
     * Data type: route to the list of processor types in the pipe
     */
    private static final Map<Class<? extends PipelineContext>, List<Class<? extends CreateHandler<? extends PipelineContext>>>> CREATE_PIPELINE_MAP =
        new HashMap<>(8);
    private static final Map<Class<? extends PipelineContext>, List<Class<? extends ReleaseHandler<? extends PipelineContext>>>> RELEASE_PIPELINE_MAP =
        new HashMap<>(8);
    private static final Map<Class<? extends PipelineContext>, List<Class<? extends CheckHandler<? extends PipelineContext>>>> CHECK_PIPELINE_MAP =
        new HashMap<>(8);

    /*
     * Configure the processing pipeline for the various context types
     */
    static {
        CHECK_PIPELINE_MAP.put(ClusterCheckContext.class, Arrays.asList(
            K8sOpsHandler.class,
            QueueOpsHandler.class));

        CREATE_PIPELINE_MAP.put(ClusterCreateContext.class, Arrays.asList(
            K8sOpsHandler.class,
            NamespaceOpsHandler.class,
            QueueOpsHandler.class
        ));

        RELEASE_PIPELINE_MAP.put(ClusterReleaseContext.class, Arrays.asList(
            K8sOpsHandler.class,
            QueueOpsHandler.class));

        // other pipe configurations
    }

    private ApplicationContext appContext;

    /**
     * The corresponding pipeline mapping is generated from the routing table when Spring starts
     */
    @Bean("createPipelineMap")
    public Map<Class<? extends PipelineContext>, List<? extends CreateHandler<? extends PipelineContext>>> getCreateHandlerMap() {
        return CREATE_PIPELINE_MAP.entrySet().stream()
            .collect(Collectors.toMap(Map.Entry::getKey, this::toCreatePipeline));
    }

    @Bean("checkPipelineMap")
    public Map<Class<? extends PipelineContext>, List<? extends CheckHandler<? extends PipelineContext>>> getCheckHandlerMap() {
        return CHECK_PIPELINE_MAP.entrySet().stream()
            .collect(Collectors.toMap(Map.Entry::getKey, this::toCheckPipeline));
    }

    /**
     * Release
     */
    @Bean("releasePipelineMap")
    public Map<Class<? extends PipelineContext>, List<? extends ReleaseHandler<? extends PipelineContext>>> getReleaseHandlerMap() {
        return RELEASE_PIPELINE_MAP.entrySet().stream()
            .collect(Collectors.toMap(Map.Entry::getKey, this::toReleasePipeline));
    }

    /**
     * Builds the pipe from the list of ContextHandler types in the given pipe
     */
    private List<? extends CreateHandler<? extends PipelineContext>> toCreatePipeline(
        Map.Entry<Class<? extends PipelineContext>, List<Class<? extends CreateHandler<? extends PipelineContext>>>> entry) {
        return entry.getValue().stream().map(appContext::getBean).collect(Collectors.toList());
    }

    private List<? extends ReleaseHandler<? extends PipelineContext>> toReleasePipeline(
        Map.Entry<Class<? extends PipelineContext>, List<Class<? extends ReleaseHandler<? extends PipelineContext>>>> entry) {
        return entry.getValue().stream().map(appContext::getBean).collect(Collectors.toList());
    }

    private List<? extends CheckHandler<? extends PipelineContext>> toCheckPipeline(
        Map.Entry<Class<? extends PipelineContext>, List<Class<? extends CheckHandler<? extends PipelineContext>>>> entry) {
        return entry.getValue().stream().map(appContext::getBean).collect(Collectors.toList());
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        appContext = applicationContext;
    }
}
